﻿using Microsoft.Extensions.Logging;
using PluginInterface;
using System.Diagnostics;
using System.Net;
using System.Reflection;

namespace Device
{
    public class DeviceService : IDisposable
    {
        //private readonly object _lock = new();
        private readonly CancellationTokenSource _tokenSource = new CancellationTokenSource();
        private readonly ILogger _logger;
        public readonly IDriver _driver;
        public readonly DeviceInfo _device;
        //private Interpreter? _interpreter;
        private bool IsReadOver = false;
        internal List<MethodInfo> Methods { get; set; }
        private Task? _task;
        private Action<List<DriverReturnValueModel>> actionBack;
        public DeviceService(IDriver driver, ILogger logger, DeviceInfo device, Action<List<DriverReturnValueModel>> actionBack = null)
        {
            this.actionBack = actionBack;
            //_interpreter = new Interpreter();
            _logger = logger;
            _driver = driver;
            _device = device;
            Methods = _driver.GetType().GetMethods().Where(x => x.GetCustomAttribute(typeof(MethodAttribute)) != null).ToList();
            if (_device.AutoStart)
            {
                _logger.LogInformation($"线程已启动:{_device.DeviceName}");
                if (_device.DeviceVariables != null)
                {
                    List<DeviceVariable> deviceVariables = null;
                    //if (_device.DeviceVariables.Count() < 10)
                    //{
                    //    deviceVariables = _device.DeviceVariables;
                    //}
                    //else
                    //{
                    //    deviceVariables = DeviceParamGroup(_device.DeviceVariables);
                    //}
                    deviceVariables = _device.DeviceVariables;
                    foreach (var item in deviceVariables)
                    {
                        item.StatusType = VaribaleStatusTypeEnum.Bad;
                    }

                    _task = Task.Factory.StartNew(async () =>
                    {
                        // await StartCollection(deviceVariables);
                        await StartCollectionBatch(deviceVariables);
                    }, TaskCreationOptions.LongRunning);
                }
            }
        }
        private List<DeviceVariable> DeviceParamGroup(List<DeviceVariable> paramVariables)
        {
            List<DeviceVariable> deviceVariables = paramVariables.Where(t => t.Method == "ReadMultiple").ToList();
            var readMuls = paramVariables.Where(t => t.Method != "ReadMultiple").GroupBy(t => new
            {
                t.Method,
                t.DataType,
                t.EndianType
            }).Select(t => new
            {
                t.Key.Method,
                t.Key.DataType,
                t.Key.EndianType,
                StartAddress = t.Min(m => int.Parse(m.DeviceAddress)),
                EndAddress = t.Max(m => int.Parse(m.DeviceAddress))
            }).ToList();
            var pageSize = 125;
            foreach (var readMul in readMuls)
            {
                var startAddress = readMul.StartAddress;
                var endAddress = readMul.EndAddress;
                var count = endAddress - startAddress;
                var pageCount = count / pageSize;
                for (var i = 0; i <= pageCount; i++)
                {
                    var length = 125;
                    var _startAddress = startAddress + i * pageSize;
                    if (_startAddress + 125 > endAddress)
                    {
                        length = endAddress - _startAddress + 1;
                    }
                    else
                    {
                        length = 125;
                    }
                    if (readMul.Method == "HoldingRegisters")
                    {
                        var key = $"m_{_startAddress}_{i}";
                        var multipleAddress1 = new DeviceVariable
                        {
                            Name = $"3,{readMul.StartAddress},{key}号参数",
                            Method = "ReadMultiple",
                            DataType = readMul.DataType,
                            EndianType = readMul.EndianType,
                            DeviceAddress = $"3,{_startAddress},{length},{key}"
                        };
                        deviceVariables.Add(multipleAddress1);
                    }
                }
            }
            return deviceVariables;
        }
        public async Task StartCollection(List<DeviceVariable> deviceVariables)
        {
            var deviceVariablesGroup = deviceVariables.Where(x => x.ProtectType != ProtectTypeEnum.WriteOnly).OrderBy(o => o.Index).GroupBy(x => x.Alias).Select(t => new DriverDeviceVariablesModel
            {
                Alias = t.Key,
                ioarg = t.Select(tt => new DriverAddressIoArgModel
                {
                    Name = tt.Name,
                    Address = tt.DeviceAddress,
                    ValueType = tt.DataType,
                    EndianType = tt.EndianType
                }).ToList(),
                DeviceVariables = t.ToList()
            }).ToList();
            var minPeriod = deviceVariables!.Any() ? (int)_driver.MinPeriod : 10000;
            var cmdPeriod = (int)_device.CmdPeriod;
            //上传客户端属性
            while (true)
            {
                if (_tokenSource.IsCancellationRequested)
                {
                    _logger.LogInformation($"停止线程:{_device.DeviceName}");
                    return;
                }
                //lock (_lock)
                {
                    Stopwatch stopwatch = Stopwatch.StartNew();
                    try
                    {
                        if (_driver.IsConnected)
                        {
                            stopwatch.Start();
                            foreach (var deviceVariableGroup in deviceVariablesGroup)
                            {
                                string deviceName = string.IsNullOrWhiteSpace(deviceVariableGroup.Alias)
                                                ? _device.DeviceName
                                                : deviceVariableGroup.Alias;
                                //var payLoad = new PayLoad() { Values = new() };
                                if (deviceVariableGroup.DeviceVariables != null)
                                {
                                    foreach (var item in deviceVariableGroup.DeviceVariables)
                                    {
                                        if (cmdPeriod > 0)
                                        {
                                            await Task.Delay(cmdPeriod);
                                        }
                                        var ioarg = deviceVariableGroup.ioarg[deviceVariableGroup.DeviceVariables.IndexOf(item)];
                                        var method = Methods.FirstOrDefault(x => x.Name == item.Method);
                                        if (method != null)
                                        {
                                            var rets = (List<DriverReturnValueModel>)method.Invoke(_driver,
                                                            new object[] { ioarg })!;
                                            foreach (var ret in rets)
                                            {
                                                Console.WriteLine($"名称：{ret.Name} 地址：{ret.Address} 值：{ret.Value} 时间：{ret.Timestamp}");
                                            }
                                            if (actionBack != null)
                                            {
                                                actionBack(rets);
                                            }
                                            //Console.WriteLine($"{item.Name} 值：{ret.Value}");
                                        }
                                        //var obj = _interpreter.Eval(item.Expressions);
                                    }
                                }
                            }
                        }
                        else
                        {
                            if (_driver.Connect())
                            {

                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, $"线程循环异常,{_device.DeviceName}");
                    }
                }
                //Thread.Sleep(_device.DeviceVariables!.Any() ? (int)_driver.MinPeriod : 10000);
                if (minPeriod > 0)
                {
                    await Task.Delay(minPeriod);
                }
            }
        }
        private ushort GetModbusReadCount(DataTypeEnum dataType)
        {
            if (dataType.ToString().Contains("32") || dataType.ToString().Contains("Float"))
                return 2;
            if (dataType.ToString().Contains("64") || dataType.ToString().Contains("Double"))
                return 4;
            return 1;
        }
        private List<DriverBatchAddressIoArgModel> DeviceParamBatch(List<DeviceVariable> paramVariables)
        {
            var items = new List<DriverBatchAddressIoArgModel> { };
            var deviceVariables = paramVariables.Where(t => t.Method != "ReadMultipleV2").ToList();

            var pageSize = 125;
            var batchCount = 0;

            var pageIndex = 0;
            while (true)
            {
                var pageCount = 0;

                var item = new DriverBatchAddressIoArgModel { DriverAddressIoArgs = new List<DriverAddressIoArgModel> { } };
                if (pageIndex >= deviceVariables.Count())
                {
                    break;
                }
                var startAddress = deviceVariables[pageIndex].DeviceAddress;
                var nextParamAddress = int.Parse(deviceVariables[pageIndex].DeviceAddress);
                for (; pageIndex < deviceVariables.Count(); pageIndex++)
                {
                    var deviceVariable = deviceVariables[pageIndex];
                    int paramLength = GetModbusReadCount(deviceVariable.DataType);

                    if (batchCount + paramLength > pageSize || nextParamAddress != int.Parse(deviceVariable.DeviceAddress))
                    {
                        var key = $"m_{pageCount}";
                        item.Address = $"3,{startAddress},{batchCount},{key}";
                        items.Add(item);
                        //pageIndex--;
                        batchCount = 0;
                        break;
                    }
                    else
                    {
                        item.DriverAddressIoArgs.Add(new DriverAddressIoArgModel
                        {
                            Name = deviceVariable.Name,
                            Address = deviceVariable.DeviceAddress,
                            EndianType = deviceVariable.EndianType,
                            ValueType = deviceVariable.DataType
                        });
                        batchCount += paramLength;
                    }
                    if (pageIndex == deviceVariables.Count() - 1)
                    {
                        var key = $"m_{pageCount}";
                        item.Address = $"3,{startAddress},{batchCount},{key}";
                        items.Add(item);
                    }
                    nextParamAddress = int.Parse(deviceVariable.DeviceAddress) + paramLength;
                }
                pageCount++;
            }
            return items;
        }
        public async Task StartCollectionBatch(List<DeviceVariable> deviceVariables)
        {
            var driverBatchAddressIoArgs = DeviceParamBatch(deviceVariables);
            var deviceVariablesGroup = deviceVariables.Where(x => x.ProtectType != ProtectTypeEnum.WriteOnly).OrderBy(o => o.Index).Select(t => new DriverAddressIoArgModel
            {
                Address = t.DeviceAddress,
                ValueType = t.DataType,
                EndianType = t.EndianType
            }).ToList();
            var minPeriod = deviceVariables!.Any() ? (int)_driver.MinPeriod : 10000;
            var cmdPeriod = (int)_device.CmdPeriod;
            //上传客户端属性
            while (true)
            {
                if (_tokenSource.IsCancellationRequested)
                {
                    _logger.LogInformation($"停止线程:{_device.DeviceName}");
                    return;
                }
                //lock (_lock)
                {
                    Stopwatch stopwatch = Stopwatch.StartNew();
                    try
                    {
                        IsReadOver = false;
                        if (_driver.IsConnected)
                        {
                            stopwatch.Start();
                            foreach (var item in driverBatchAddressIoArgs)
                            {
                                if (cmdPeriod > 0)
                                {
                                    await Task.Delay(cmdPeriod);
                                }
                                var method = Methods.FirstOrDefault(x => x.Name == "ReadMultipleV2");
                                if (method != null)
                                {
                                    var rets = (List<DriverReturnValueModel>)method.Invoke(_driver,
                                                    new object[] { item })!;
                                    foreach (var ret in rets)
                                    {
                                        Console.WriteLine($"{ret.Address} 值：{ret.Value} 时间：{ret.Timestamp}");
                                    }
                                    if (actionBack != null)
                                    {
                                        actionBack(rets);
                                    }
                                    //Console.WriteLine($"{item.Name} 值：{ret.Value}");
                                }
                                //var obj = _interpreter.Eval(item.Expressions);
                            }
                        }
                        else
                        {
                            if (_driver.Connect())
                            {

                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, $"线程循环异常,{_device.DeviceName}");
                    }
                    finally
                    {
                        IsReadOver = true;
                    }
                }
                //Thread.Sleep(_device.DeviceVariables!.Any() ? (int)_driver.MinPeriod : 10000);
                if (minPeriod > 0)
                {
                    await Task.Delay(minPeriod);
                }
            }
        }
        public void StopCollection()
        {
            while (!IsReadOver)
            {

            }
            _logger.LogInformation($"线程停止:{_device.DeviceName}");
            if (_task == null) return;
            _tokenSource.Cancel();
            _driver.Close();
        }
        public void Dispose()
        {
            StopCollection();
            _driver.Dispose();
            //_interpreter = null;
            Methods = null;
            _logger.LogInformation($"线程释放,{_device.DeviceName}");
        }
    }
}